﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

public static class Validation
{
    private const string _sessionKey = "Validator.WebForms.Messages";
    public static List<string> ValidationMessages
    {
        get
        {
            if (HttpContext.Current.Session[_sessionKey] as List<string> == null)
                HttpContext.Current.Session[_sessionKey] = new List<string>();
            return (List<string>)HttpContext.Current.Session[_sessionKey];
        }
    }

    public static void DisplayValidationError(this ITextControl source, string message)
    {
        var ctrl = source as WebControl;
        if (ctrl == null)
            return;

        ctrl.Attributes["data-validator-invalid"] = message;
        ctrl.Attributes["data-validator-invalidval"] = source.Text;
        ctrl.Page.ClientScript.RegisterStartupScript(ctrl.Page.GetType(), "error_" + ctrl.ID, "$('#" + ctrl.ClientID + "').highlight();", true);

        //display message at top in case missed at control
        ValidationMessages.Add(message);
    }

    /// <summary>
    /// To perform server validation (as a backup to client side validation) call this method on postback in the MasterPage Page_Load method.
    /// </summary>
    public static void PerformValidation(System.Web.UI.Page Page)
    {
        //get postback control
        Control ctrl = null;

        if (!String.IsNullOrEmpty(Page.Request.Params["__EVENTTARGET"]))
            ctrl = Page.FindControl(Page.Request.Params["__EVENTTARGET"]);

        for (int i = 0; ctrl == null && i < Page.Request.Form.Count; i++)
        {
            if (String.IsNullOrEmpty(Page.Request.Form.Keys[i]))
                continue;

            string id = Page.Request.Form.Keys[i];

            // handle ImageButton controls ...
            if (id.EndsWith(".x") || id.EndsWith(".y"))
                id = id.Substring(0, id.Length - 2);

            var c = Page.FindControl(id);
            if (c is System.Web.UI.WebControls.Button || c is System.Web.UI.WebControls.ImageButton)
                ctrl = c;
        }

        //if postback ctrl not a button skip valiator checks
        if (ctrl as IButtonControl == null)
            return;

        //retrive validation group if exists otherwise return
        var validationGroup = ctrl.GetType().GetProperty("OnClientClick").GetValue(ctrl, null).ToString();
        var match = Regex.Match(validationGroup, @"validate\('(.*?)'\)");
        if (match.Groups.Count < 2 || match.Groups[1].Value == "")
            return;
        validationGroup = match.Groups[1].Value;

        bool isvalid = true;

        //validate all controls
        foreach (string id in Page.Request.Form.AllKeys.Where(k => !String.IsNullOrEmpty(k)))
        {
            var field = Page.FindControl(id);
            System.Web.UI.AttributeCollection attr = null;
            var disabled = true;
            var value = Page.Request.Form[id];

            var webCtrlField = field as WebControl;
            if (webCtrlField != null)
            {
                attr = webCtrlField.Attributes;
                disabled = !webCtrlField.Enabled;
            }

            var htmlCtrlField = field as HtmlControl;
            if (htmlCtrlField != null)
            {
                attr = htmlCtrlField.Attributes;
                disabled = htmlCtrlField.Disabled;
            }

            //do not validate if param is not a control, or control is disabled, or validationgroup is different
            if (disabled || !validationGroup.Equals(attr["data-validator-group"], StringComparison.CurrentCultureIgnoreCase))
                continue;

            string type = null;

            if (String.IsNullOrEmpty(value))
            {
                if (!String.IsNullOrEmpty(attr["data-validator-required"]))
                    type = "data-validator-required";
            }
            else
            {
                if (!String.IsNullOrEmpty(attr["data-validator-regular"]) && !String.IsNullOrEmpty(attr["data-validator-validexpress"]) && !Regex.IsMatch(value, attr["data-validator-validexpress"]))
                    type = "data-validator-regular";

                if (!String.IsNullOrEmpty(attr["data-validator-regular"]) && !String.IsNullOrEmpty(attr["data-validator-invalidexpress"]) && Regex.IsMatch(value, attr["data-validator-invalidexpress"]))
                    type = "data-validator-regular";

                if (!String.IsNullOrEmpty(attr["data-validator-compare"]) && !String.IsNullOrEmpty(attr["data-validator-compareto"]) && value != Page.Request.Form[attr["data-validator-compareto"].Replace('_', '$')])
                    type = "data-validator-compare";

                if (!String.IsNullOrEmpty(attr["data-validator-invalid"]) && !String.IsNullOrEmpty(attr["data-validator-invalidval"]) && value == attr["data-validator-invalidval"])
                    type = "data-validator-invalid";

                if (!String.IsNullOrEmpty(attr["data-validator-email"]))
                {
                    try { new System.Net.Mail.MailAddress(value); }
                    catch { type = "data-validator-email"; }
                }
            }

            if (type != null)
            {
                isvalid = false;
                ValidationMessages.Add(attr[type]);
            }
        }

        //if failed validation, cancel click or command event by removing the event handler
        if (!isvalid)
        {
            var click = ctrl.GetType().GetEvent("Click");
            var command = ctrl.GetType().GetEvent("Command");

            var flags = BindingFlags.Instance | BindingFlags.NonPublic;
            var eh = (EventHandlerList)ctrl.GetType().GetProperty("Events", flags).GetValue(ctrl, null);
            var entry = eh.GetType().GetField("head", flags).GetValue(eh);

            while (entry != null)
            {
                var handler = (Delegate)entry.GetType().GetField("handler", flags).GetValue(entry);
                try { click.RemoveEventHandler(ctrl, handler); }
                catch { }
                try { command.RemoveEventHandler(ctrl, handler); }
                catch { }
                entry = entry.GetType().GetField("next", flags).GetValue(entry);
            }
        }
    }
}
